﻿using System.Linq;
using System.Web.Mvc;
using CID.Domain.Development;
using CID.Domain.Operations;
using CID.Tests.Support;
using CID.Web.Controllers;
using CID.Web.Models.Development;
using NailsFramework.IoC;
using NailsFramework.Persistence;
using NailsFramework.UserInterface.TestingSupport;
using NUnit.Framework;
using Rhino.Mocks;

namespace CID.Web.Tests.Controllers
{
    [TestFixture(Description="This class test the functionality for managing versions from the ComponentController")]
    class ComponentControllerTests : BaseWebTests
    {
        [Inject]
        public IBag<Component> Components { private get; set; }
        [Inject]
        public IBag<Version> Versions { private get; set; }
        [Inject]
        public IBag<Environment> Environments { private get; set; }
        
        private ComponentController controller;
        private Component component;

        public override void SetUp()
        {
            base.SetUp();
            controller = TestControllers.Get<ComponentController>();
            var requestContext = StubsBuilder.BuildRequestContextStub();
            controller.ControllerContext = new ControllerContext(requestContext, controller);
            controller.Url = new UrlHelper(requestContext);
            
            component = Components.First();
        }


        [Test]
        public void Versions_ShouldCreateDefaultViewWithVersionsOfSelectedComponent()
        {
            var result = controller.Versions(component.Id);

            Assert.IsNotNull(result);
            Assert.AreEqual("", result.ViewName);
            Assert.AreEqual(component, result.Model);
        }

        [Test]
        public void DetailsVersion_ShouldCreateDefaultViewWithSelectedVersion()
        {
            var model = component.Versions.First();
            var viewModel = new VersionViewModel(model);

            var result = controller.DetailsVersion(model.Id);

            Assert.IsNotNull(result);
            Assert.AreEqual("", result.ViewName);
            Assert.IsInstanceOf<VersionViewModel>(result.Model);
            Assert.AreEqual(model.Id, ((VersionViewModel)result.Model).Id);
        }

        #region EditVersion

        [Test]
        public void GetEditVersion_ShouldCreateDefaultViewWithSelectedEntity()
        {
            var model = component.Versions.First();
            var viewModel = new VersionViewModel(model);

            var result = controller.EditVersion(model.Id);

            Assert.IsNotNull(result);
            Assert.AreEqual("", result.ViewName);
            Assert.IsInstanceOf<VersionViewModel>(result.Model);
            Assert.AreEqual(model.Id, ((VersionViewModel)result.Model).Id);
        }

        [Test]
        public void PostEditVersion_ValidData_ShouldUpdateEntityAndRedirectToVersions()
        {
            var model = component.Versions.First();
            var viewModel = MockRepository.GenerateStub<VersionViewModel>(model, Environments.ToArray());

            var result = controller.EditVersion(viewModel.Id, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
            Assert.AreEqual("Versions", ((RedirectToRouteResult)result).RouteValues["action"]);
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }
        
        [Test]
        public void PostEditVersion_ValidDataWithAjaxRequest_ShouldUpdateEntityAndRedirectToVersions()
        {
            StubsBuilder.StubRequestAsAjaxRequest(controller.ControllerContext.RequestContext.HttpContext.Request);
            var model = component.Versions.First();
            var viewModel = MockRepository.GenerateStub<VersionViewModel>(model, Environments.ToArray());

            var result = controller.EditVersion(viewModel.Id, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<JsonResult>(result);
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }

        [Test]
        public void PostEditVersion_InvalidData_ShouldReturnEditVersionView()
        {
            var model = component.Versions.First();
            var viewModel = MockRepository.GenerateStub<VersionViewModel>(model, Environments.ToArray());
            controller.ModelState.AddModelError("", "");

            var result = controller.EditVersion(viewModel.Id, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<ViewResult>(result);
            Assert.AreEqual("", ((ViewResult)result).ViewName);
            Assert.IsInstanceOf<VersionViewModel>(((ViewResult)result).Model);
            Assert.AreEqual(viewModel, ((ViewResult)result).Model);
            viewModel.AssertWasNotCalled(x => x.UpdateModel(model));
        }

        [Test]
        public void PostEditVersion_DomainExceptionThrown_ShouldReturnEditVersionView()
        {
            var model = component.Versions.First();
            var viewModel = MockRepository.GenerateStub<VersionViewModel>(model, Environments.ToArray());
            viewModel.Stub(x => x.UpdateModel(model)).Throw(new DomainExceptionStub());

            var result = controller.EditVersion(viewModel.Id, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<ViewResult>(result);
            Assert.AreEqual("", ((ViewResult)result).ViewName);
            Assert.IsInstanceOf<VersionViewModel>(((ViewResult)result).Model);
            Assert.AreEqual(viewModel, ((ViewResult)result).Model);
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }
        #endregion

        #region CreateVersion
        [Test]
        public void GetCreateVersion_ShouldCreateDefaultViewWithNewEntity()
        {
            var result = controller.CreateVersion(component.Id);

            Assert.IsNotNull(result);
            Assert.AreEqual("", result.ViewName);
            Assert.IsInstanceOf<VersionViewModel>(result.Model);
            Assert.AreEqual(0, ((VersionViewModel)result.Model).Id);
        }

        [Test]
        public void PostCreateVersion_ValidData_ShouldCreateEntityAndRedirectToVersions()
        {
            var originalVersionsCount = component.Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();

            var result = controller.CreateVersion(viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
            Assert.AreEqual("Versions", ((RedirectToRouteResult)result).RouteValues["action"]);
            var model = component.Versions.Skip(originalVersionsCount).FirstOrDefault();
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }

        [Test]
        public void PostCreateVersion_ValidDataWithAjaxRequest_ShouldCreateEntityAndGetJsotThatRedirectToVersions()
        {
            StubsBuilder.StubRequestAsAjaxRequest(controller.ControllerContext.RequestContext.HttpContext.Request);
            var originalVersionsCount = component.Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();

            var result = controller.CreateVersion(viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<JsonResult>(result);
            var model = component.Versions.Skip(originalVersionsCount).FirstOrDefault();
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }

        [Test]
        public void PostCreateVersion_InvalidData_ShouldReturnCreateVersionView()
        {
            var originalVersionsCount = Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();
            controller.ModelState.AddModelError("", "");

            var result = controller.CreateVersion(viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<ViewResult>(result);
            Assert.AreEqual("", ((ViewResult)result).ViewName);
            Assert.IsInstanceOf<VersionViewModel>(((ViewResult)result).Model);
            Assert.AreEqual(viewModel, ((ViewResult)result).Model);
            Assert.AreEqual(originalVersionsCount, Versions.Count());
            viewModel.AssertWasNotCalled(x => x.UpdateModel(((IVersion)null)), x => x.IgnoreArguments());
        }

        [Test]
        public void PostCreateVersion_DomainExceptionThrown_ShouldReturnCreateVersionView()
        {
            var originalVersionsCount = Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();
            viewModel.Stub(x => x.UpdateModel(null)).IgnoreArguments().Throw(new DomainExceptionStub());

            var result = controller.CreateVersion(viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<ViewResult>(result);
            Assert.AreEqual("", ((ViewResult)result).ViewName);
            Assert.IsInstanceOf<VersionViewModel>(((ViewResult)result).Model);
            Assert.AreEqual(viewModel, ((ViewResult)result).Model);
            Assert.AreEqual(originalVersionsCount, Versions.Count());
            viewModel.AssertWasCalled(x => x.UpdateModel(((IVersion)null)), x => x.IgnoreArguments());
        }

        #endregion

        #region CreateVersionCopy
        [Test]
        public void GetCreateVersionCopy_ShouldCreateCreateViewWithNewEntity()
        {
            var version = component.Versions.First();
            
            var result = controller.CreateVersionCopy(version.Id);
            var resultModel = result.Model as VersionViewModel;

            Assert.IsNotNull(result);
            Assert.AreEqual("CreateVersion", result.ViewName);
            Assert.IsInstanceOf<VersionViewModel>(result.Model);
            Assert.AreEqual(0, resultModel.Id);
            Assert.IsTrue(string.IsNullOrEmpty(resultModel.Name));
            Assert.IsTrue(string.IsNullOrEmpty(resultModel.Description));
        }

        [Test]
        public void PostCreateVersionCopy_ValidData_ShouldCreateEntityAndRedirectToVersions()
        {
            var originalVersionsCount = component.Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();

            var result = controller.CreateVersionCopy(1, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
            Assert.AreEqual("Versions", ((RedirectToRouteResult)result).RouteValues["action"]);
            var model = component.Versions.Skip(originalVersionsCount).FirstOrDefault();
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }

        [Test]
        public void PostCreateVersionCopy_ValidDataWithAjaxRequest_ShouldCreateEntityAndRedirectToVersions()
        {
            StubsBuilder.StubRequestAsAjaxRequest(controller.ControllerContext.RequestContext.HttpContext.Request);
            var originalVersionsCount = component.Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();

            var result = controller.CreateVersionCopy(1, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<JsonResult>(result);
            var model = component.Versions.Skip(originalVersionsCount).FirstOrDefault();
            viewModel.AssertWasCalled(x => x.UpdateModel(model));
        }

        [Test]
        public void PostCreateVersionCopy_InvalidData_ShouldReturnCreateVersionView()
        {
            var originalVersionsCount = Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();
            controller.ModelState.AddModelError("", "");

            var result = controller.CreateVersionCopy(1, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<ViewResult>(result);
            Assert.AreEqual("CreateVersion", ((ViewResult)result).ViewName);
            Assert.IsInstanceOf<VersionViewModel>(((ViewResult)result).Model);
            Assert.AreEqual(viewModel, ((ViewResult)result).Model);
            Assert.AreEqual(originalVersionsCount, Versions.Count());
            viewModel.AssertWasNotCalled(x => x.UpdateModel(((IVersion)null)), x => x.IgnoreArguments());
        }

        [Test]
        public void PostCreateVersionCopy_DomainExceptionThrown_ShouldReturnCreateVersionView()
        {
            var originalVersionsCount = Versions.Count();
            var viewModel = ViewModelStubBuilder.BuildViewModel<VersionViewModel, IVersion>();
            viewModel.Stub(x => x.UpdateModel(null)).IgnoreArguments().Throw(new DomainExceptionStub());

            var result = controller.CreateVersionCopy(1, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<ViewResult>(result);
            Assert.AreEqual("CreateVersion", ((ViewResult)result).ViewName);
            Assert.IsInstanceOf<VersionViewModel>(((ViewResult)result).Model);
            Assert.AreEqual(viewModel, ((ViewResult)result).Model);
            Assert.AreEqual(originalVersionsCount, Versions.Count());
            viewModel.AssertWasCalled(x => x.UpdateModel(((IVersion)null)), x => x.IgnoreArguments());
        }

        #endregion

        #region DeleteVersion
        [Test]
        public void GetDeleteVersion_ShouldCreateDefaultViewWithSelectedEntity()
        {
            var model = component.Versions.First();
            var viewModel = new VersionViewModel(model);

            var result = controller.DeleteVersion(model.Id);

            Assert.IsNotNull(result);
            Assert.AreEqual("", result.ViewName);
            Assert.IsInstanceOf<VersionViewModel>(result.Model);
            Assert.AreEqual(model.Id, ((VersionViewModel)result.Model).Id);
        }

        [Test]
        public void PostDeleteVersion_ValidData_ShouldDeleteEntityAndRedirectToVersions()
        {
            var model = component.Versions.First();
            var viewModel = MockRepository.GenerateStub<VersionViewModel>(model, Environments.ToArray());

            var result = controller.DeleteVersion(viewModel.Id, viewModel);

            Assert.IsNotNull(result);
            Assert.IsInstanceOf<RedirectToRouteResult>(result);
            Assert.AreEqual("Versions", ((RedirectToRouteResult)result).RouteValues["action"]);
            CollectionAssert.DoesNotContain(component.Versions.ToArray(), model);
        }
        #endregion
    }
}
